/*
 <one line to give the library's name and an idea of what it does.>
 Copyright (C) 2013  Guillaume <email>
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */


#ifndef MSTREE_H
#define MSTREE_H

#include <msRegister.h>
#include <msLogger.h>
#include <msAttribute_Child.h>

namespace atomism
{
    
    /*! \class msTreeMapper
     *
     *  \brief Interface to map the objects structure into a tree database
     *
     * \section presentation Presentation
     *
     * The primary purpose of msTreeMapper is to provide load/save functions
     * from/to files for the objects deriving from it. Also, it
     * allows to define a deep copier (defined as operator=) from this class,
     * so that derived classes don't have to take care of it.
     *
     * The philosophy here is to declare all the Degrees Of Freedom (DoF)
     * of your objects (i.e. all the members defining completely the object's state);
     * after that msTreeMapper is able to manage the database.  \n
     * There are three types of degrees of freedom you can use:
     *      - an attribute (msAttribute): this correspond to simple structure (int, double,
     * float, vector, map ... ). To declare one type as an attribute, a lexical cast of
     * this type needs to exist; see msAttribute.
     *      - a child (msChild) : this corresponds to members derived
     * from msTreeMapper
     *      - a vector of child (msChildren) : this correspond to a
     * vector that contains derived object from a particular msTreeMapper derived type.
     *
     * \section registration Registration of the degrees of freedom
     *
     * The registration of the childs and attributes is realized by overriding the
     * function **void initialize()**. In case other members of your class depends
     * on the DoFs, you need to override the function **void update()**:
     * its purpose is to update the non DoF members from the DoF members.
     * Also, the constructors of the derived class have to call the
     * constructVar method to declare the class. \n
     * To clarify these features, lets look at the next example showing how it
     * would work for a class containing as DoF:
     *    - a list of integer,
     *    - a msSampler
     *    - a vector of msScalarFunction
     
     ~~~~~~~~~~~~~{.cpp}
     #include <msScalarFunction.h>
     #include <msSampler.h>
     
     myClass : public msTreeMapper
     {
     public:
     
     // //--------- mandatory methods for a good database mapping
     
     myClass() { constructVar("myClass","default_variable_name","default_id");
     }
     
     void  initialize() {
     
     msTreeMapper::initialize()
     declareAttribute(                   ListOfIntegers,                   "ListOfIntegers");
     declareChild<msSampler>(            TheSampler,     msSampler::New(), "TheSampler" );
     declareChildren<msScalarFunction>(  TheVectorOfScalarFct,             "TheVectorOfScalarFct");
     }
     
     void  update() {
     
     msTreeMapper::update()
     if( ListOfIntegers.size() > 0 ) FirstElementOfListOfIntegers = ListOfIntegers[0];
     else                            FirstElementOfListOfIntegers = -1;
     }
     
     // //--------- you should always define them, it is always the same
     
     static boost::shared_ptr<myClass> New() {
     
     boost::shared_ptr<myClass> T( new myClass );
     T->initialize();
     T->update();
     return T;
     }
     
     virtual boost::shared_ptr<msTreeMapper> clone() {
     
     boost::shared_ptr<msTreeMapper> clone = New();
     clone->msTreeMapper::operator=(*this);
     return clone;
     }
     
     // //--------- the myClass stuffs
     
     void addInteger(int a){
     
	 ListOfIntegers.push_back(a);
     }
     
     void setSampler(boost::shared_ptr<msSampler> newSampler) {
     
     msTreeMapper::update(TheSampler,newSampler)
     }
     
     void addFunctionToSample(boost::shared_ptr<msSampler> newFct){
     
     msTreeMapper::addElementToChildren(TheVectorOfScalarFct,newFct);
     }
     
     private:
     
     // //--------- Degrees of freedom
     
     vector<int> 				ListOfIntegers;
     msChild<msSampler>			TheSampler;            //  can be any type deriving from msSampler
     msChildren<msScalarFunction>	  	TheVectorOfScalarFct;  //  can store any type deriving from msScalarFunction
     
     // //--------- this is not a DoF, but say we need it for some purpose -> initialize it in the update function
     
     int FirstElementOfListOfIntegers;
     
     }
     ~~~~~~~~~~~~~
     *
     * **Some important comments:**
     *    - the instance of *myClass* is the parent of **ListOfIntegers**, **TheSampler**
     * and **TheVectorOfScalarFct**
     *    - the idea is to minize the construction tasks in the default constructor,
     * complex initialization should go in the *initialize* method
     *    - **initialize** and **update** methods should always call the corresponding
     * parent class methods
     *    - the static **New** function, and the **clone** are not mandatory at this point, however
     * for consistency and to avoid problem they should be added
     *    - child and children are mapped using the templated msChild and msChildren class respectively
     *    - msChild and msChildren work only with shared_ptr (this ensure a proper
     * managment of the memory )
     *    - the string argument providen in the *declare* family of methods has to be unique, you should
     * use the same name as the C++  variable to avoid confusion
     * /n
     * The database structure can be displayed when calling the print function of
     * msTreeMapper. Let's construct an object:
     *
     ~~~~~~~~~~~~~{.cpp}
     boost::shared_ptr<myClass> myobject = myClass::New();
     
     myobject->setSampler(msLinearSampler::New());
     
     myobject->addFunctionToSample(msParser::New());
     
     myobject->addInteger(5);
     
     myobject->print(cout);
     ~~~~~~~~~~~~~
     *
     * The output will be:
     ~~~~~~~~~~~~~{.xml}
     myClass-default_variable_name Id="default_id" ListOfIntegers="5"
     msLinearSampler-TheSampler       # here is the msLinearSampler description of attributes and childs #
     msParser-TheVectorOfScalarFct_0  # here is the msParser description of attributes and childs #
     ~~~~~~~~~~~~~
     
     
     
     
     * \section update How to use the degrees of freedom
     *
     * Now the question is how to use the DoF defined, short answer is as usual (well, almost).
     * The purpose for msTreeMapper is to always ensure the synchronisation between objects
     * and database structure.
     *
     * \subsection attr Attribute
     *
     * For the attribute type of DoF  (atomism::msAttribute), there is no restriction: just use
     * it how you want to. \n
     *
     * \subsection child Child
     *
     * The child type of DoF (atomism::msChild) is manipulated as a pointer on the templated type.
     * The only difference with usual pointers is when you want to change the adress where the child
     * is pointing to.
     * In this case your are not able to use the assignement operator (this will break at the
     * compile time), but you can do it using the function matomism::msTreeMapper::updateChild.
     *
     * \subsection children Children
     *
     * Similarly, *children* type of DoF (atomism::msChildren) can be manipulated as usual
     * pointer on vector, but modification on the number of element can be done only using the functions
     * atomism::msTreeMapper::addElementToChildren and atomism::msTreeMapper::clearChildren.
     */
    class msTreeMapper : public msRegister , public boost::enable_shared_from_this<msTreeMapper>
    {
        
    public:
        
        //! @Name From msRegister
        //@{
        static msRegistrar Registrar;
        static msRegister*  create() {return new msTreeMapper;}
        static bool isTreeMapperRegistredInPython;
        virtual void registryInPython();
        //@}
        
        struct VarDatabase
        {friend class msTreeMapper;
            std::string Name; std::string Id;
            VarDatabase(){};
            VarDatabase(std::string name, std::string type,std::string id){
                Name=name;Type=type;Id=id;
            }
        private:
            std::string Type;
        };
        
        typedef std::map<std::string , msChildBase* >::iterator childIterator;
        typedef std::map<std::string , msChildBase* >::const_iterator const_childIterator;
        
        static boost::shared_ptr<msTreeMapper> New() {
            
            boost::shared_ptr<msTreeMapper> T( new msTreeMapper );
            T->initialize();
            T->mySharedPtr()=T;
            return T;
        }
        
        //! return a deep copy of the structure
        virtual boost::shared_ptr<msTreeMapper> clone() {
            
            boost::shared_ptr<msTreeMapper> clone = New();
            clone->msTreeMapper::operator=(*this);
            return clone;
        }
        
        
    public:
        
        //! declare the attributes & childs of the tree structure. see @ref registration
        virtual void initialize() { declareAttribute(VariableAttributs.Id,"Id");
            //declareAttribute();
        };
        
        //! update the class members from the  attributes & childs of the tree structure. see @ref registration
        virtual void update() {
            LOGGER_ENTER_FUNCTION_DBG("void msTreeMapper::update()",getFullId());
            LOGGER_WRITE(msLogger::DEBUG,"update object "+getFullId());
            LOGGER_EXIT_FUNCTION();
	    }
        
        
        
        msTreeMapper();
        
        msTreeMapper& operator=(const msTreeMapper& rhs);   //!< deep copy assignement
        
        boost::shared_ptr<msTreeMapper> deepCopy();  	//!< return a deep copy of the object
        
        
        //! @Name Function to operate on the children / attributes
        //@{
        //! return the string cast of the attribute of 'id'
        std::string getAttributeInString(std::string id )  ;
        
        //! return the first child matching the given 'id'
        boost::shared_ptr<msTreeMapper> getChild(const std::string& id) const;
        
        std::vector< boost::shared_ptr<msTreeMapper> > getChildren() const;
        
        std::vector< boost::shared_ptr<msTreeMapper> > getChildrenDerivedFrom(std::string type) const;
        
        //! add a child 'child' with variable name 'varName', exception if variable name already exists
        void addChild( boost::shared_ptr<msTreeMapper> child, std::string varName);
        
        //! add a child 'child' with variable name 'varName', replace a previous item if one already exist under varName
        void replaceChild( boost::shared_ptr<msTreeMapper> child, std::string varName);
        
        std::vector< std::string> getAttributes() const ;
        
        boost::shared_ptr<msTreeMapper> setAttribute(const std::string& id,const std::string& value);
        
        //@}
        
        
        //! \name  Type, id and affiliation
        //@{
        std::string getType() const; 		//!< return last derived class name
        
        std::string getId()   const;		//!< return the Id
        
        std::string getFullId()   const;		//!< return Affiliation+Id
        
        std::string getVariableName( const boost::shared_ptr<const msTreeMapper> child )   const;	//!< return variable name
        
        std::string getVariableName( boost::shared_ptr<msTreeMapper> child ) const;	//!< return variable name
        
        std::string getFullVariableName()   const;	//!< return variable name chain from root
        
        boost::shared_ptr<msTreeMapper> getParent() const {
            
            boost::shared_ptr<msTreeMapper> ptr;
            if( (ptr=Parent.lock()) ) return ptr;
            
            BOOST_THROW_EXCEPTION( msError("Can not lock the Parent weak pointer"
                                           ,"boost::shared_ptr<msTreeMapper> msTreeMapper::getParent() "
                                           ,getFullId() ) );
            
        }  	    //!< return the Parent
        
        boost::shared_ptr<msTreeMapper> setId(std::string id);			    //!< set the Id
        
        void setAffiliation(boost::shared_ptr<msTreeMapper> parent){
            Parent.reset();
            Parent = boost::weak_ptr<msTreeMapper>(parent->mySharedPtr());
            boost::shared_ptr<msTreeMapper> ptr;
            if( !(ptr=Parent.lock()) )
                
                BOOST_THROW_EXCEPTION( msError("Can not lock the Parent weak pointer"
                                               ,"void msTreeMapper::setAffiliation(boost::shared_ptr<msTreeMapper> parent)"
                                               ,getFullId() ) );
        } //!< set the parent object
        
        //! return 1 if the object is derived from the class 'target'
        bool isDerivedFrom(std::string target) const;
        
        //! return 1 if the object has a parent
        bool hasParent() const { boost::shared_ptr<msTreeMapper> ptr;
            if( (ptr=Parent.lock()) ) return 1;
            return 0;
        }
        bool hasChildOfName(const string name ) { return Children.find(name)!=Children.end();
        }
        bool isAffiliatedTo(boost::shared_ptr<msTreeMapper> parent) { boost::shared_ptr<msTreeMapper> p = Parent.lock();
            return p == parent;
        }
        //@}
        
        ~msTreeMapper(){};
        
        virtual std::ostream& print(std::ostream&) const;   //!< print the object
        
        
    protected:
        
        VarDatabase VariableAttributs;     	//!< Attributs of the variable: name, type, id
        
        //! Set the variable attributes, should be called from the constructors of the derived class
        void constructVar(std::string type,std::string var,std::string id);
        
        //boost::shared_ptr<msTreeMapper> getSharedPtr(){ return boost::shared_from_this(); };
        
        
        //! \name Functions to declare the Attribut/Objects(childs) in the database
        //@{
        /** declare an attribute.
         * \param elem attribute
         * \param key  variable name
         */
        template<class T>
        void declareAttribute( T& elem , std::string key );
        
        /** declare a Child.
         * \param refchild reference on the child
         * \param name  variable name you want for the child, should be the same as its name in the c++ object
         */
        template<class T>
        void declareChild(msChild<T>& refchild, boost::shared_ptr<T> value, std::string name);
        
        /** declare a Children.
         * \param refchild reference on the children
         * \param name  variable name you want for the children, should be the same as its name in the c++ object
         */
        template<class T>
        void declareChildren( msChildren<T>& refvec,std::string name);
        
        //@}
        //! \name Functions to update the childs declared in the database from new objects
        //@{
        
        /** update the adress pointed by a child declared to a new one
         * \param lhschild reference on the child previsously declared
         * \param rhschild the adress of the new child
         */
        template<class T>
        void update(msChild<T>& lhschild,boost::shared_ptr<T> rhschild);
        
        /** add a child to a children vector, behave like std::vector::add
         * \param vector reference on the children vector you want to complete
         * \param elem the adress of the new child to add
         */
        template<class T>
        void addElementToChildren(msChildren<T>& vector,boost::shared_ptr<T> elem);
        
        /** clear a children vector, behave like std::vector::clear
         * \param vector reference on the children vector you want to complete
         */
        template<class T>
        void clearChildren(msChildren<T>& vector);
        
        /** resize a children vector, behave like std::vector::resize
         * \param vector reference on the children vector you want to complete
         * \param n the new size
         */
        template<class T>
        void resizeChildren(msChildren<T>& vector,size_t n){vector.resize(n);}
        //@}
        
        void writeAffiliation(std::ostream& out) const;
        
        void writeAffiliationVariable(std::ostream& out) const;
        
        boost::shared_ptr<msTreeMapper> mySharedPtr() {
            
            try{ boost::shared_ptr<msTreeMapper> me = shared_from_this();
                return me;
	        }
            catch(...){}
            return boost::shared_ptr<msTreeMapper>(); };
        
    private:
        
        boost::weak_ptr<msTreeMapper>  Parent;
        
        std::map< std::string , boost::shared_ptr<msAttributeBase> >	Attributes;
        
        std::map< std::string , msChildBase*  >             Children;
        std::map< std::string , msChildrenBase * >          RegistredVectorsOfChildren; //!< short cut for 'Vector values' child acess
        
        std::string getSymbolicLinkPath(boost::shared_ptr<msTreeMapper> scan ) const;
        
        std::ostream& printTree(std::ostream&,size_t& level,std::string varName) const;
        
        std::string CppType;		    	//!< full cpp type: Base:Derived1:Derived2 ...
        
        size_t getNextIndiceInChildren(std::string vecname); //!< indice of the next element of a container of id 'vecname'
        
    };
    
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::declareAttribute( T& elem , std::string key )
    {
        Attributes[key] = boost::shared_ptr<  msAttribute<T> >( new msAttribute<T> (elem) );
    }
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::declareChild(msChild<T>& refchild, boost::shared_ptr<T> value, std::string name) {
        
        LOGGER_ENTER_FUNCTION_DBG("void msTreeMapper::declareChild(msChild<T>& refchild, T* value, std::string name)",getFullId());
        LOGGER_WRITE(msLogger::DEBUG,"Name of the child: "+name);
        
        if( !value->isDerivedFrom("msTreeMapper") ) {
            LOGGER_WRITE(msLogger::ERROR,"The child you want to register is not derived from msTreeMapper");
            assert(0);
        }
        refchild.ptr = value;
        value->VariableAttributs.Name = name;
        try{
            mySharedPtr()    = shared_from_this();
            refchild->Parent = mySharedPtr();
            Children[name]   = &refchild;
            //RegistredChildren[name]   = refchild.get();
            std::stringstream out;
            out<<"child passed: "<<static_cast<void*>(&refchild)<<" adress in children: "<<static_cast<void*>(&Children[name])
               <<" adress of the object:"<<refchild.get();
            LOGGER_WRITE(msLogger::DEBUG,out.str());
        }
        catch( boost::exception &e )
        {
            Children[name]   = &refchild;
            //RegistredChildren[name]= refchild.get();
            LOGGER_EXIT_FUNCTION();
            return;
        }
        
        LOGGER_EXIT_FUNCTION();
    }
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::declareChildren( msChildren<T>& refvec,std::string name) {
        
        LOGGER_ENTER_FUNCTION_DBG("void msTreeMapper::declareChildren( msChildren<T>& refvec,std::string name) ",getFullId());
        LOGGER_WRITE(msLogger::DEBUG,"Name of the children: "+name);
        typedef typename msChildren<T>::iterator Iterator;
        Iterator it = refvec.Elements.begin();
        RegistredVectorsOfChildren[name]=&refvec;
        LOGGER_EXIT_FUNCTION();
    }
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::update(msChild<T>& lhschild,boost::shared_ptr<T> rhschild) {
        
        LOGGER_ENTER_FUNCTION_DBG("void msTreeMapper::update(msChild<T>& lhschild,boost::shared_ptr<T> rhschild)",getFullId());
        LOGGER_WRITE(msLogger::DEBUG,"Id of the updated child:"+lhschild->getFullId());
        
        if( lhschild.getSharedPtr() == boost::shared_ptr<T>() )
            BOOST_THROW_EXCEPTION( msError("the original (lhschild) child is not initialized, did you declare it?",
                                           "void msTreeMapper::update(msChild<T>& lhschild,boost::shared_ptr<T> rhschild)",
                                           getFullId()
                                           ));
        
        childIterator it = Children.begin(); 
        std::string varname="";
        
        for(;it!= Children.end();++it)
            if( it->second->getBasePtr() == lhschild.getBasePtr() )  varname = it->first;
        
        if( varname=="")
            BOOST_THROW_EXCEPTION( msError("ref. to lhschild no found",
                                           "void msTreeMapper::update(msChild<T>& lhschild,boost::shared_ptr<T> rhschild)",
                                           getFullId()
                                           ));
        
        //lhschild.operator=(rhschild);
        
        lhschild.set(rhschild);
        rhschild->VariableAttributs.Name = varname;
        Children[varname]->set( boost::static_pointer_cast<msTreeMapper>(rhschild) );
        
        LOGGER_EXIT_FUNCTION();
    }
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::addElementToChildren(msChildren<T>& vector,boost::shared_ptr<T> elem) {
        
        LOGGER_ENTER_FUNCTION_DBG("msTreeMapper::addElementToChildren(msChildren<T>& vector,boost::shared_ptr<T> elem)",getFullId());
        
        std::map< std::string , msChildrenBase * >::iterator  it;
        it=RegistredVectorsOfChildren.begin();
        std::string varname="";
        for(;it!= RegistredVectorsOfChildren.end();++it)
            if( it->second == &vector )  varname = it->first; 
        
        LOGGER_WRITE(msLogger::DEBUG,"elem's id: "+elem->getId()+"; vector name: "+varname);
        
        if( varname=="")
            BOOST_THROW_EXCEPTION( msError("reference to the children not found, did you declare it in the constructor ?"
                                           ,"msTreeMapper::addElementToChildren(msChildren<T>& vector,boost::shared_ptr<T> elem)"
                                           ,getFullId() ) ); 
        
        vector.push_back(elem);
        std::stringstream name;name<<varname<<"_"<<getNextIndiceInChildren(varname);
        elem->VariableAttributs.Name=name.str();
        LOGGER_WRITE(msLogger::DEBUG,"child's variable name: "+name.str());
        
        Children[name.str()] = new msChild<T>();
        Children[name.str()]->set(elem);
        
        LOGGER_EXIT_FUNCTION();
    }
    
    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
    
    template<class T>
    void msTreeMapper::clearChildren(msChildren<T>& vector) {
        
        LOGGER_ENTER_FUNCTION_DBG("msTreeMapper::clearChildren(msChildren<T>& vector)",getFullId());
        
        std::map< std::string , msChildrenBase * >::iterator  it;
        it=RegistredVectorsOfChildren.begin();
        std::string varname="";
        std::vector<std::string> strs;
        
        for(;it!= RegistredVectorsOfChildren.end();++it)
            if( it->second == &vector )  varname = it->first;
        
        if( varname=="")
            BOOST_THROW_EXCEPTION( msError("reference to the children not found, did you declare it?"
                                           ,"void msTreeMapper::clearChildren(msChildren<T>& vector)"
                                           ,getFullId() ) );

        LOGGER_WRITE(msLogger::DEBUG,"Variable name of the vector: " + varname);
            
        vector.clear();
        
        for( const_childIterator it=Children.begin();
            it!=Children.end(); it++) {
            
            boost::split(strs, it->first, boost::is_any_of("_"));
            LOGGER_WRITE(msLogger::DEBUG,"erase element " + it->first );
            if( strs[0] == varname ) Children.erase(it->first);
            }

            /*
        for( size_t i=0; i< getNextIndiceInChildren(varname) ; i++) {
            std::stringstream name;name<<varname<<"_"<<i;
            LOGGER_WRITE(msLogger::DEBUG,"erase element " + name.str() );
            
            if(Children.find(name.str())==Children.end())
                BOOST_THROW_EXCEPTION( msError("the child "+ name.str() + " does not exist in Children"
                                               ,"void msTreeMapper::clearChildren(msChildren<T>& vector)"
                                               ,getFullId() ) );
            Children.erase(name.str());
        }*/
        LOGGER_EXIT_FUNCTION();
    }
    
} //end of atomism 


#endif // MSREGISTRATORS_H
